"""
1、初始化主窗口：
创建tk.Tk()实例作为主窗口。
设置窗口标题为“二维码生成器”。
定义颜色主题和字体样式。
2、定义全局变量：
qr_images: 用于存储生成的二维码图片路径。
qr_texts: 用于存储与每个二维码对应的文本信息。
temp_folder: 指定一个临时文件夹来保存生成的二维码图片。
zoom_window 和 current_index: 用于管理放大查看二维码的功能。
3、定义函数：
zoom_qr_code(index): 放大指定索引位置的二维码。
close_zoom_window(): 关闭放大窗口。
prev_qr_code(event) 和 next_qr_code(event): 分别用于切换至上一张或下一张二维码。
get_user_input(): 获取用户的输入文本及循环次数。
generate_single_qr_code(input_text, index): 生成单个二维码。
display_qr_and_text(qr_frame, tk_image, input_text, row_idx, col_idx, index): 在界面上显示二维码及其对应的文本。
generate_qr_code(): 核心功能，根据用户输入生成一系列二维码，并将它们展示在界面上。
save_to_word(), save_to_pdf(), save_to_excel(): 将生成的所有二维码保存到Word文档、PDF文件或Excel表格中。
on_mousewheel(event) 和 on_shift_mousewheel(event): 处理鼠标滚轮事件以实现垂直和水平滚动。
on_entry_change(*args): 监听输入框变化，当检测到特定分隔符时禁用循环次数输入框。
cleanup_temp_folder(): 清理临时文件夹中的所有文件。
4、构建用户界面：
添加标签、输入框、按钮等控件。
配置Canvas和Scrollbars以支持滚动查看大量二维码。
绑定事件处理器到相应的控件上。
5、运行主循环：
调用root.mainloop()启动Tkinter的事件循环，等待用户交互。
6、清理工作：
当用户关闭窗口时，通过root.protocol("WM_DELETE_WINDOW", ...)绑定的回调函数清理临时文件夹并销毁主窗口。
这个程序从用户输入开始，经过处理生成多个二维码，然后把这些二维码可视化地展示出来，并允许用户进行进一步的操作如保存至不同格式的文件中。
整个过程涉及图形界面操作、图像处理以及文件I/O等多个方面。
"""

import tkinter as tk
from tkinter import messagebox, Canvas, Scrollbar, Frame, Event
import qrcode
from PIL import Image, ImageTk
from docx import Document
from docx.shared import Inches
import os
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from reportlab.lib.utils import ImageReader
from datetime import datetime
from openpyxl import Workbook
from openpyxl.drawing.image import Image as ExcelImage


# 定义放大二维码的函数

def zoom_qr_code(index):
    global zoom_window, current_index
    if zoom_window:
        zoom_window.destroy()
    current_index = index
    zoom_window = tk.Toplevel(root)
    zoom_window.title('放大二维码')
    zoom_window.configure(bg=BG_COLOR)
    # 加载原始图片并放大
    img = Image.open(qr_images[index])
    # 缩小放大倍数，例如改为2倍
    img = img.resize((img.width * 2, img.height * 2), Image.Resampling.LANCZOS)
    tk_image = ImageTk.PhotoImage(img)
    # 显示放大的二维码
    label = tk.Label(zoom_window, image=tk_image, bg=LABEL_BG)
    label.image = tk_image
    label.pack(pady=10)
    # 显示对应的文本
    text_label = tk.Label(zoom_window, text=qr_texts[index], bg=LABEL_BG, font=FONT)
    text_label.pack(pady=10)

    # 添加切换按钮
    button_frame = tk.Frame(zoom_window, bg=BG_COLOR)
    button_frame.pack(pady=10)
    prev_button = tk.Button(button_frame, text="上一张", command=lambda: prev_qr_code(None), bg=BUTTON_BG, fg=BUTTON_FG,
                            font=FONT)
    prev_button.pack(side=tk.LEFT, padx=10)
    next_button = tk.Button(button_frame, text="下一张", command=lambda: next_qr_code(None), bg=BUTTON_BG, fg=BUTTON_FG,
                            font=FONT)
    next_button.pack(side=tk.LEFT, padx=10)

    # 绑定键盘事件
    zoom_window.bind('<Left>', prev_qr_code)
    zoom_window.bind('<Right>', next_qr_code)


# 定义关闭放大窗口的函数

def close_zoom_window():
    global zoom_window
    if zoom_window:
        zoom_window.destroy()
        zoom_window = None


# 定义切换到上一个二维码的函数

def prev_qr_code(event):
    global current_index
    if current_index > 0:
        current_index -= 1
        # 避免重复创建窗口
        if not zoom_window:
            zoom_qr_code(current_index)
        else:
            # 加载原始图片并放大
            img = Image.open(qr_images[current_index])
            img = img.resize((img.width * 2, img.height * 2), Image.Resampling.LANCZOS)
            tk_image = ImageTk.PhotoImage(img)
            # 更新显示的二维码
            for widget in zoom_window.winfo_children():
                if isinstance(widget, tk.Label) and hasattr(widget, 'image'):
                    widget.config(image=tk_image)
                    widget.image = tk_image
                elif isinstance(widget, tk.Label) and widget.cget('text') != qr_texts[current_index]:
                    widget.config(text=qr_texts[current_index])
            # 保存当前窗口位置
            current_x, current_y = zoom_window.winfo_x(), zoom_window.winfo_y()
            # 更新窗口内容后恢复位置
            zoom_window.geometry('+{}+{}'.format(current_x, current_y))
            zoom_window.focus_set()


# 定义切换到下一个二维码的函数

def next_qr_code(event):
    global current_index
    if current_index < len(qr_images) - 1:
        current_index += 1
        # 避免重复创建窗口
        if not zoom_window:
            zoom_qr_code(current_index)
        else:
            # 加载原始图片并放大
            img = Image.open(qr_images[current_index])
            img = img.resize((img.width * 2, img.height * 2), Image.Resampling.LANCZOS)
            tk_image = ImageTk.PhotoImage(img)
            # 更新显示的二维码
            for widget in zoom_window.winfo_children():
                if isinstance(widget, tk.Label) and hasattr(widget, 'image'):
                    widget.config(image=tk_image)
                    widget.image = tk_image
                elif isinstance(widget, tk.Label) and widget.cget('text') != qr_texts[current_index]:
                    widget.config(text=qr_texts[current_index])
            # 保存当前窗口位置
            current_x, current_y = zoom_window.winfo_x(), zoom_window.winfo_y()
            # 更新窗口内容后恢复位置
            zoom_window.geometry('+{}+{}'.format(current_x, current_y))
            zoom_window.focus_set()


# 定义颜色主题
BG_COLOR = "#F0F0F0"  # 背景颜色
FG_COLOR = "#333333"  # 前景颜色
BUTTON_BG = "#4CAF50"  # 按钮背景颜色
BUTTON_FG = "white"  # 按钮前景颜色
LABEL_BG = "#E0E0E0"  # 标签背景颜色
FRAME_BG = "#F0F0F0"  # 框架背景颜色

# 定义字体
FONT = ("Arial", 12)

# 全局变量，用于存储生成的二维码图片和对应的文本
qr_images = []
qr_texts = []
temp_folder = "QRCodeFolder"


# 封装获取用户输入的函数
def get_user_input():
    user_input = entry.get()
    loop_count = loop_entry.get()
    if not user_input:
        messagebox.showwarning("输入错误", "请输入要生成二维码的文本")
        return None, None
    if loop_count:
        try:
            loop_count = int(loop_count)
        except ValueError:
            messagebox.showwarning("输入错误", "循环次数必须是整数")
            return None, None
    else:
        loop_count = 1
    return user_input, loop_count


# 封装生成单个二维码的函数
def generate_single_qr_code(input_text, index):
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=8,
        border=4,
    )
    qr.add_data(input_text)
    qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white")
    img_path = os.path.join(temp_folder, f"temp_qr_{index}.png")
    img.save(img_path)
    tk_image = ImageTk.PhotoImage(img)
    return img_path, tk_image


# 封装显示二维码和文本的函数
# 全局变量，用于存储放大窗口和当前显示的二维码索引
from typing import Optional  # 导入Optional类型注解
zoom_window: Optional[tk.Toplevel] = None  # 明确zoom_window的类型为tk.Toplevel或None
current_index = 0


def display_qr_and_text(qr_frame, tk_image, input_text, row_idx, col_idx, index):
    # 创建标签用于显示二维码
    label = tk.Label(qr_frame, image=tk_image, bg=LABEL_BG)
    label.image = tk_image
    label.grid(row=row_idx * 2, column=col_idx, padx=10, pady=10)
    # 绑定点击事件
    label.bind('<Button-1>', lambda e, idx=index: zoom_qr_code(idx))
    # 创建标签用于显示文本
    text_label = tk.Label(qr_frame, text=input_text, bg=LABEL_BG, font=FONT)
    text_label.grid(row=row_idx * 2 + 1, column=col_idx, padx=10, pady=0)


# 优化后的generate_qr_code函数
def generate_qr_code():
    global qr_images, qr_texts
    user_input, loop_count = get_user_input()
    if user_input is None or loop_count is None:
        return
    # 清除之前的二维码
    for widget in qr_frame.winfo_children():
        widget.destroy()
    qr_images = []
    qr_texts = []
    # 清理临时文件夹中的图片
    if os.path.exists(temp_folder):
        for filename in os.listdir(temp_folder):
            file_path = os.path.join(temp_folder, filename)
            if os.path.isfile(file_path):
                os.remove(file_path)
    else:
        os.makedirs(temp_folder)
    # 处理逗号分隔的输入，支持英文逗号、中文逗号和中文顿号
    separators = [',', '，', '、', '/', "\\"]
    for sep in separators:
        user_input = user_input.replace(sep, ',')
    inputs = user_input.split(',')
    row_idx = 0
    col_idx = 0
    for idx, input_text in enumerate(inputs):
        input_text = input_text.strip()
        if input_text:
            if input_text.isdigit() and loop_count > 1:
                base_number = input_text  # 保持原始字符串格式
                number_length = len(base_number)  # 获取原始数字长度
                for i in range(loop_count):
                    current_number = str(int(base_number) + i)  # 计算新数字并转换为字符串
                    current_number = current_number.zfill(number_length)  # 补上前导零，保持固定长度
                    img_path, tk_image = generate_single_qr_code(current_number, len(qr_images))
                    qr_images.append(img_path)
                    qr_texts.append(current_number)
                    display_qr_and_text(qr_frame, tk_image, current_number, row_idx, col_idx, len(qr_images) - 1)

                    col_idx += 1
                    if col_idx == 5:
                        col_idx = 0
                        row_idx += 1
            else:
                img_path, tk_image = generate_single_qr_code(input_text, len(qr_images))
                qr_images.append(img_path)
                qr_texts.append(input_text)
                display_qr_and_text(qr_frame, tk_image, input_text, row_idx, col_idx, len(qr_images) - 1)
                col_idx += 1
                if col_idx == 5:
                    col_idx = 0
                    row_idx += 1
    # 调整 Frame 在 Canvas 中的位置以实现居中
    qr_frame.update_idletasks()
    frame_width = qr_frame.winfo_width()
    canvas_width = qr_canvas.winfo_width()
    x_offset = max(0, (canvas_width - frame_width) // 2)
    qr_canvas.coords(frame_window, (x_offset, 0))
    # 显示保存到 Word 和 PDF 按钮
    save_to_word_button.pack(pady=10)
    save_to_pdf_button.pack(pady=10)
    save_to_excel_button.pack(pady=10)


def save_to_word():
    global qr_images, qr_texts
    if not qr_images:
        messagebox.showwarning("提示", "没有生成二维码，请先生成二维码。")
        return

    doc = Document()
    table = doc.add_table(rows=0, cols=2)
    rows_per_page = 6  # 假设每页最多显示 6 行二维码
    current_row = 0

    for i in range(0, len(qr_images), 2):
        if current_row % rows_per_page == 0 and current_row != 0:
            doc.add_page_break()  # 插入分页符
        row_cells = table.add_row().cells
        for j in range(2):
            if i + j < len(qr_images):
                img_path = qr_images[i + j]
                text = qr_texts[i + j]
                p = row_cells[j].add_paragraph()
                r = p.add_run()
                r.add_picture(img_path, width=Inches(2))
                p = row_cells[j].add_paragraph()
                p.alignment = 1  # 居中对齐
                p.add_run(text)
        current_row += 1

    # 生成当前时间的字符串
    current_time = datetime.now().strftime("%Y%m%d%H%M")
    file_path = f"QRCode-{current_time}.docx"
    doc.save(file_path)
    messagebox.showinfo("提示", f"二维码已保存到 {file_path}")


def save_to_pdf():
    global qr_images, qr_texts
    if not qr_images:
        messagebox.showwarning("提示", "没有生成二维码，请先生成二维码。")
        return

    # 生成当前时间的字符串
    current_time = datetime.now().strftime("%Y%m%d%H%M")
    file_path = f"QRCode-{current_time}.pdf"
    pdf = canvas.Canvas(file_path, pagesize=letter)
    x_start = 50
    y = 700
    image_height = 150
    text_height = 20  # 文本高度

    for i in range(0, len(qr_images), 2):
        if y - image_height - text_height < 50:  # 检查剩余空间是否足够
            pdf.showPage()
            y = 700
        for j in range(2):
            if i + j < len(qr_images):
                img_path = qr_images[i + j]
                text = qr_texts[i + j]
                x = x_start + j * 220
                img = ImageReader(img_path)
                pdf.drawImage(img, x, y - image_height, width=150, height=image_height)
                text_width = pdf.stringWidth(text, "Helvetica", 12)
                text_x = x + (150 - text_width) / 2
                pdf.drawString(text_x, y - image_height - text_height, text)
        y -= image_height + text_height + 10  # 预留一些间距

    pdf.save()
    messagebox.showinfo("提示", f"二维码已保存到 {file_path}")


def save_to_excel():
    global qr_images, qr_texts
    if not qr_images:
        messagebox.showwarning("提示", "没有生成二维码，请先生成二维码。")
        return

    # 生成当前时间的字符串
    current_time = datetime.now().strftime("%Y%m%d%H%M")
    file_path = f"QRCode-{current_time}.xlsx"
    wb = Workbook()
    ws = wb.active

    row = 1
    col = 1
    for i in range(len(qr_images)):
        img_path = qr_images[i]
        text = qr_texts[i]

        # 插入二维码图片
        img = ExcelImage(img_path)
        ws.add_image(img, f'{chr(64 + col)}{row}')

        # 插入文本
        ws.cell(row=row + 1, column=col, value=text)

        # 根据图片大小调整行高和列宽
        img_width = img.width
        img_height = img.height
        # 调整列宽，这里乘以一个系数以确保有足够空间
        ws.column_dimensions[chr(64 + col)].width = img_width / 7
        # 调整图片所在行的行高
        ws.row_dimensions[row].height = img_height / 1.3
        # 调整文本所在行的行高
        ws.row_dimensions[row + 1].height = 20

        col += 1
        if col > 2:
            col = 1
            row += 3

    wb.save(file_path)
    messagebox.showinfo("提示", f"二维码已保存到 {file_path}")


def on_mousewheel(event: Event):
    # 滚轮事件处理
    qr_canvas.yview_scroll(-1 * (event.delta // 120), "units")


def on_shift_mousewheel(event: Event):
    # 按住Shift键时水平滚动
    qr_canvas.xview_scroll(-1 * (event.delta // 120), "units")


def on_entry_change(*args):
    separators = [',', '，', '、', '/', "\\"]
    if any(sep in entry.get() for sep in separators):
        loop_entry.config(state='disabled')
    else:
        loop_entry.config(state='normal')


def cleanup_temp_folder():
    global temp_folder
    if os.path.exists(temp_folder):
        for filename in os.listdir(temp_folder):
            file_path = os.path.join(temp_folder, filename)
            if os.path.isfile(file_path):
                os.remove(file_path)
        os.rmdir(temp_folder)


# 创建主窗口
root = tk.Tk()
root.title("二维码生成器")
root.configure(bg=BG_COLOR)

# 创建标题
title_label = tk.Label(root, text="输入文本（支持中英文逗号、顿号、正反斜杠分隔）:", bg=BG_COLOR, fg=FG_COLOR, font=FONT)
title_label.pack(pady=10)

# 创建文本输入框
entry_var = tk.StringVar()
entry_var.trace_add('write', on_entry_change)
entry = tk.Entry(root, width=50, textvariable=entry_var, font=FONT, bd=2, relief=tk.SOLID)
entry.pack(pady=10)

# 创建循环次数输入框
loop_label = tk.Label(root, text="循环次数（可选）:", bg=BG_COLOR, fg=FG_COLOR, font=FONT)
loop_label.pack()
loop_entry = tk.Entry(root, width=50, font=FONT, bd=2, relief=tk.SOLID)
loop_entry.pack(pady=10)

# 创建生成按钮
generate_button = tk.Button(root, text="生成二维码", command=generate_qr_code, bg=BUTTON_BG, fg=BUTTON_FG, font=FONT)
generate_button.pack(pady=10)

# 创建保存到 Word 按钮
save_to_word_button = tk.Button(root, text="保存到 Word", command=save_to_word, bg=BUTTON_BG, fg=BUTTON_FG, font=FONT)

# 创建保存到 PDF 按钮
save_to_pdf_button = tk.Button(root, text="保存到 PDF", command=save_to_pdf, bg=BUTTON_BG, fg=BUTTON_FG, font=FONT)

# 创建保存到 Excel 按钮
save_to_excel_button = tk.Button(root, text="保存到 Excel", command=save_to_excel, bg=BUTTON_BG, fg=BUTTON_FG,
                                 font=FONT)

# 创建可滚动的Canvas
qr_canvas = Canvas(root, bg=BG_COLOR)
qr_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)

# 创建垂直Scrollbar
scrollbar_y = Scrollbar(root, orient=tk.VERTICAL, command=qr_canvas.yview)
scrollbar_y.pack(side=tk.RIGHT, fill=tk.Y, padx=10)

# 创建水平Scrollbar
scrollbar_x = Scrollbar(root, orient=tk.HORIZONTAL, command=qr_canvas.xview)
scrollbar_x.pack(side=tk.BOTTOM, fill=tk.X, pady=10)

# 配置Canvas的滚动
qr_canvas.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
qr_canvas.bind('<Configure>', lambda e: qr_canvas.configure(scrollregion=qr_canvas.bbox("all")))

# 绑定滚轮事件到整个 Canvas
qr_canvas.bind_all("<MouseWheel>", on_mousewheel)
qr_canvas.bind_all("<Shift-MouseWheel>", on_shift_mousewheel)

# 创建一个Frame来放置二维码
qr_frame = Frame(qr_canvas, bg=FRAME_BG)
frame_window = qr_canvas.create_window((0, 0), window=qr_frame, anchor="nw")


def on_frame_configure(event):
    # 更新滚动区域以适应Frame的内容
    qr_canvas.configure(scrollregion=qr_canvas.bbox("all"))
    # 调整 Frame 在 Canvas 中的位置以实现居中
    qr_frame.update_idletasks()
    frame_width = qr_frame.winfo_width()
    canvas_width = qr_canvas.winfo_width()
    x_offset = max(0, (canvas_width - frame_width) // 2)
    qr_canvas.coords(frame_window, (x_offset, 0))


qr_frame.bind("<Configure>", on_frame_configure)

# 绑定窗口关闭事件，清理临时文件夹
root.protocol("WM_DELETE_WINDOW", lambda: [cleanup_temp_folder(), root.destroy()])

# 运行主循环
root.mainloop()
